﻿#include <bitset>
#include <QTimer>
#include <QDebug>
#include <QFile>
#include <QDir>
#include <QJsonDocument>
#include <PluginsAction.h>
#include "RecordPlugins.h"


QStringView RecordPlugins::name() const {
	return u"RecordLogs";
}

bool RecordPlugins::load(QString const& cfgFile /*= QString()*/) {
	if (!m_timer) {
		m_timer = new QTimer(this);
		connect(m_timer, &QTimer::timeout, this, &RecordPlugins::onWrite);
		m_timer->start(1000);
	}
	QFile file(QDir::currentPath() + cfgFile);
	if (!file.open(QIODevice::ReadOnly)) {
		emit errors(file.errorString(), name().toString());
		return false;
	}
	QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
	file.close();
	m_dbClient = new DatabaseWriter(this);
	connect(m_dbClient, &DatabaseWriter::hasError, [this](QString const& text) { emit errors(text, name().toString()); });
	auto result = m_dbClient->loadConfigs(doc.object());
	m_messageMap["changedConnectionState"] = std::bind(&RecordPlugins::updateConnectionState, this, std::placeholders::_1);
	if (result) {
		emit loadFinished(name().toString());
		return result;
	}
	return false;
}

void RecordPlugins::logsEvent(QJsonObject const& logsObj) {
	if (logsObj.contains("1") && logsObj.contains("124")) {
		QTimer::singleShot(1, [logsObj, this]()->void {
			m_dbClient->writeLogs(logsObj);
		});
	}
}

void RecordPlugins::sensorsEvent(QJsonObject const& valuesObj) {
	for (auto it = std::cbegin(valuesObj); it != std::cend(valuesObj); it++) {
		m_sensorsValues[it.key()] = it.value();
	}
	if (!m_sensorsValues.contains("25")
		|| !m_sensorsValues.contains("0")
		|| !m_sensorsValues.contains("66")
		|| !m_sensorsValues.contains("27")
		|| !m_sensorsValues.contains("1")) {
		return;
	}
	quint16 value = quint16(m_sensorsValues["25"].toInt());
	if ((value & 0xff) || (value >> 8)) {
		if (!m_cutTimer.isValid()) {
			m_cutTimer.start();
		}
		if (!m_isCut) {
			m_isCut = true;
		}
	}
	else {
		if (m_cutTimer.isValid()) {
			m_cutElapsed += m_cutTimer.elapsed();
			m_cutTimer.invalidate();
		}
		if (m_isCut) {
			m_isCut = false;
		}
	}
	value = quint16(m_sensorsValues["0"].toInt());
	std::bitset<sizeof(quint16) * 8> bits(value);
	value = quint16(m_sensorsValues["66"].toInt());
	std::bitset<sizeof(quint16) * 8> bits2(value);
	if ((bits.test(1) || bits2.test(0) || bits2.test(1)) && m_isCut) {
		if (!m_remoteTimer.isValid()) {
			m_remoteTimer.start();
		}
		if (!m_isRemote) {
			m_isRemote = true;
		}
	}
	else {
		if (m_remoteTimer.isValid()) {
			m_remoteElapsed += m_remoteTimer.elapsed();
			m_remoteTimer.invalidate();
		}
		if (m_isRemote) {
			m_isRemote = false;
		}
	}
	value = quint16(m_sensorsValues["27"].toInt());
	if (value & 0xff) {
		if (!m_oilpumpTimer.isValid()) {
			m_oilpumpTimer.start();
		}
		if (!m_isOilpumpRunning) {
			m_isOilpumpRunning = true;
		}
	}
	else {
		if (m_oilpumpTimer.isValid()) {
			m_oilpumpElapsed += m_bootTimer.elapsed();
			m_oilpumpTimer.invalidate();
		}
		if (m_isOilpumpRunning) {
			m_isOilpumpRunning = false;
		}
	}
	value = quint16(m_sensorsValues["1"].toInt());
	bits = value;
	if (
		((bits.test(1) && bits.test(5))
		|| (bits.test(4) && bits.test(5))
		|| (bits.test(4) && bits.test(6)))
		&& m_isCut) {

		if (!m_autoTimer.isValid()) {
			m_autoTimer.start();
		}
		if (!m_isAutoCut) {
			m_isAutoCut = true;
		}
	}
	else {
		if (m_autoTimer.isValid()) {
			m_autoElapsed += m_autoTimer.elapsed();
			m_autoTimer.invalidate();
		}
		if (m_isAutoCut) {
			m_isAutoCut = false;
		}
	}
}

QStringList RecordPlugins::subTopic() const {
	return QStringList() << "logs" << "sensors" << "others";
}

void RecordPlugins::handleMessage(QStringView topic, QByteArray payload) {
	auto topics = subTopic();
	if (!topics.contains(topic)) {
		return;
	}
	auto doc = QJsonDocument::fromJson(payload);
	auto const&& obj = doc.object();
	auto const&& command = obj["command"].toString();
	auto it = m_messageMap.find(command);
	if (it != std::cend(m_messageMap)) {
		std::invoke(it->second, obj);
	}
}

QStringList RecordPlugins::commands() const {
	return QStringList() << "changedConnectionState";
}

void RecordPlugins::onWrite() {
	writeCutDur();
	writeRemoteDur();
	writeBootDur();
	writeOilpumpDur();
	writeAutoDur();

	if (m_sensorsValues.isEmpty()) {
		return;
	}
	for (int i = 0; i < 200; i++) {
		if (!m_sensorsValues.contains(QString::number(i))) {
			return;
		}
	}
	m_dbClient->writeSensorsValues(m_sensorsValues);
	m_sensorsValues = QJsonObject();
}

void RecordPlugins::updateConnectionState(QJsonObject const& obj) {
	m_isConnected = ConnectionState{ obj["state"].toInt() } == ConnectionState::ConnectedState;
	if (m_isConnected) {
		if (!m_bootTimer.isValid()) {
			m_bootTimer.start();
		}
	}
	else {
		if (m_bootTimer.isValid()) {
			m_bootElapsed += m_bootTimer.elapsed();
			m_bootTimer.invalidate();
		}
	}
}

void RecordPlugins::writeCutDur() {
	if (!m_isCut) return;
	if (!m_cutTimer.isValid()) {
		m_cutTimer.start();
	}
	else {
		m_cutElapsed += m_cutTimer.restart();
	}
	m_dbClient->addCutDur(QDate::currentDate(), int(m_cutElapsed));
	m_cutElapsed = 0;
}

void RecordPlugins::writeRemoteDur() {
	if (!m_isRemote) return;
	if (!m_remoteTimer.isValid()) {
		m_remoteTimer.start();
	}
	else {
		m_remoteElapsed += m_remoteTimer.restart();
	}
	m_dbClient->addRemoteDur(QDate::currentDate(), int(m_remoteElapsed));
	m_remoteElapsed = 0;
}

void RecordPlugins::writeBootDur() {
	if (!m_isConnected) return;
	if (!m_bootTimer.isValid()) {
		m_bootTimer.start();
	}
	else {
		m_bootElapsed += m_bootTimer.restart();
	}
	m_dbClient->addBootDur(QDate::currentDate(), int(m_bootElapsed));
	m_bootElapsed = 0;
}

void RecordPlugins::writeOilpumpDur() {
	if (!m_isOilpumpRunning) return;
	if (!m_oilpumpTimer.isValid()) {
		m_oilpumpTimer.start();
	}
	else {
		m_oilpumpElapsed += m_oilpumpTimer.restart();
	}
	m_dbClient->addOilpumpDur(QDate::currentDate(), int(m_oilpumpElapsed));
	m_oilpumpElapsed = 0;
}

void RecordPlugins::writeAutoDur() {
	if (!m_isAutoCut) return;
	if (!m_autoTimer.isValid()) {
		m_autoTimer.start();
	}
	else {
		m_autoElapsed += m_autoTimer.restart();
	}
	m_dbClient->addAutoCutDur(QDate::currentDate(), int(m_autoElapsed));
	m_autoElapsed = 0;
}

extern "C" {
	RECORDPLUGINS_EXPORT Plugin*  make_plugin(PluginAction* pa) {
		return new RecordPlugins(pa);
	}
}